package com.socialrss.utils.foursquarekit;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.ExecutionException;

import android.os.AsyncTask;
import android.util.Log;

import com.socialrss.accounts.AccountOwner;
import com.socialrss.accounts.FollowedPerson;
import com.socialrss.constants.SocialNetworkNames.SocialNetKeys;
import com.socialrss.utils.SocialRssModel;

import fi.foyt.foursquare.api.FoursquareApi;
import fi.foyt.foursquare.api.Result;
import fi.foyt.foursquare.api.entities.CompactUser;
import fi.foyt.foursquare.api.entities.UserGroup;
import fi.foyt.foursquare.api.io.IOHandler;
import fi.foyt.foursquare.api.io.Method;
import fi.foyt.foursquare.api.io.MultipartParameter;
import fi.foyt.foursquare.api.io.Response;

public class FoursquareUtils {
	private static FoursquareUtils _foursquareutils;
	private FoursquareApi _fourapi;

	private FoursquareUtils(){
		AccountOwner own = SocialRssModel.Instance(null).getAccessTokenOfSocialNet(SocialNetKeys.f_sq.toString());
		String accessToken = own.getAccessToken();
		_fourapi  = new FoursquareApi(FoursquareConstants.CLIENT_ID, FoursquareConstants.CLIENT_SECRET, FoursquareConstants.CALLBACKURL,
					accessToken, new FoursqIOHandler());
	}
	/*
	 * Static instance method
	 */
	public static FoursquareUtils Instance(){
		if(_foursquareutils == null){
			if(checkAccessToken())
				_foursquareutils = new FoursquareUtils();
		}
		return _foursquareutils;
	}
	private static boolean checkAccessToken(){
		AccountOwner own = SocialRssModel.Instance(null).getAccessTokenOfSocialNet(SocialNetKeys.f_sq.toString());
		String accessToken = own.getAccessToken();
		if(accessToken == null){
			Log.e(FoursquareConstants.LOG_UTIL_ERROR, "There is no foursquare oAuthToken!");
			return false;
		}
		return true;
	}
	/**
	 * Fetch user friends and store them into database
	 */
	public void getFriends(){
		GetFriends tempth = new GetFriends();
		tempth.execute();
		try {
			tempth.get();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
	private class GetFriends extends AsyncTask<Void, Void, FollowedPerson[]> {
		@Override
		public FollowedPerson[] doInBackground(Void... params) {
			
			CompactUser[] usergroup = null;
			try {
				Result<UserGroup> friends = _fourapi.usersFriends("self");
				usergroup = friends.getResult().getItems();
				
			} catch (Exception e) {
				Log.i(FoursquareConstants.LOG_UTIL_ERROR, "Friends couldn't be retrieved!");
				e.printStackTrace();
				return null;
			}
			FollowedPerson[] tempPer = new FollowedPerson[usergroup.length];
			for (int i = 0; i < usergroup.length; i++) {
					tempPer[i] = new FollowedPerson(0, usergroup[i].getFirstName() + " " + usergroup[i].getLastName(),
							SocialNetKeys.f_sq, 0, usergroup[i].getId());
			}
			return tempPer;
		}
		protected void onPostExecute(FollowedPerson[] result) {
			if(result != null){
				SocialRssModel.Instance(null).insertListOfConnections(result, SocialNetKeys.f_sq);
				Log.i(FoursquareConstants.LOG_UTIL_INFO, "Foursquare connections are inserted into database.");
			}
		}
	};
	private class FoursqIOHandler extends IOHandler {

		  @Override
		  public Response fetchData(String url, Method method) {
		    int code = 200;

		    try {
		      URL aUrl = new URL(url);
		      HttpURLConnection connection = (HttpURLConnection) aUrl.openConnection();
		      try {
    			Log.i(FoursquareConstants.LOG_INFO, "Opening url : " + aUrl.toString());
		        connection.setDoInput(true);
		        //connection.setDoOutput(true);
		        connection.setRequestMethod(method.name());
		        connection.connect();
		        
		        code = connection.getResponseCode();
		        if (code == 200) {
		          InputStream inputStream = connection.getInputStream();
		          return new Response(readStream(inputStream), code, connection.getResponseMessage());
		        } else {
		          return new Response("", code, getMessageByCode(code));
		        }

		      } finally {
		        connection.disconnect();
		      }
		    } catch (MalformedURLException e) {
		      return new Response("", 400, "Malformed URL: " + url);
		    } catch (IOException e) {
		      return new Response("", 500, e.getMessage());
		    }
		  }

		  @Override
		  public Response fetchDataMultipartMime(String url, MultipartParameter... parameters) {
		    int code = 200;

		    try {
		      URL aUrl = new URL(url);
		      HttpURLConnection connection = (HttpURLConnection) aUrl.openConnection();
		      try {
		        connection.setDoInput(true);
		        connection.setDoOutput(true);
		        connection.setRequestMethod("POST");
		        connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
		        connection.connect();

		        OutputStream outputStream = connection.getOutputStream();
		        
		        StringBuffer startBoundaryBuilder = new StringBuffer("--")
		          .append(BOUNDARY)
		          .append("\r\n");
		        
		        outputStream.write(startBoundaryBuilder.toString().getBytes());
		        
		        for (MultipartParameter parameter : parameters) {
		          StringBuffer formDataBuilder = new StringBuffer()
		            .append("Content-Disposition: form-data; name=\"")
		            .append(parameter.getName())
		            .append("\"; filename=\"")
		            .append(parameter.getName())
		            .append("\"\r\n")
		            .append("Content-Type: ")
		            .append(parameter.getContentType())
		            .append("\r\n\r\n");
		          outputStream.write(formDataBuilder.toString().getBytes());
		          outputStream.write(parameter.getContent());
		        }
		        
		        StringBuilder endBoundaryBuilder = new StringBuilder("\r\n--")
		          .append(BOUNDARY)
		          .append("--\r\n");
		        outputStream.write(endBoundaryBuilder.toString().getBytes());

		        outputStream.flush();
		        outputStream.close();

		        code = connection.getResponseCode();
		        if (code == 200) {
		          InputStream inputStream = connection.getInputStream();
		          return new Response(readStream(inputStream), code, connection.getResponseMessage());
		        } else {
		          return new Response("", code, getMessageByCode(code));
		        }

		      } finally {
		        connection.disconnect();
		      }
		    } catch (MalformedURLException e) {
		      return new Response("", 400, "Malformed URL: " + url);
		    } catch (IOException e) {
		      return new Response("", 500, e.getMessage());
		    }
		  }

		  /**
		   * Reads input stream and returns it's contents as String
		   * 
		   * @param inputStream input stream to be readed
		   * @return Stream's content
		   * @throws IOException 
		   */
		  private String readStream(InputStream inputStream) throws IOException {
		    StringWriter responseWriter = new StringWriter();

		    char[] buf = new char[1024];
		    int l = 0;

		    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
		    while ((l = inputStreamReader.read(buf)) > 0) {
		      responseWriter.write(buf, 0, l);
		    }

		    responseWriter.flush();
		    responseWriter.close();
		    return responseWriter.getBuffer().toString();
		  }

		  /**
		   * Returns message for code
		   * 
		   * @param code code
		   * @return Message
		   */
		  private String getMessageByCode(int code) {
		    switch (code) {
		      case 400:
		        return "Bad Request";
		      case 401:
		        return "Unauthorized";
		      case 403:
		        return "Forbidden";
		      case 404:
		        return "Not Found";
		      case 405:
		        return "Method Not Allowed";
		      case 500:
		        return "Internal Server Error";
		      default:
		        return "Unknown";
		    }
		  }

		  private static final String BOUNDARY = "----------gc0p4Jq0M2Yt08jU534c0p";
		}
}

